gint minimum_width;
gint natural_width;
+ /* For "has-entry" specific behavior we track
+ * an automated cell renderer and text column */
+ gint text_column;
+ GtkCellRenderer *text_renderer;
+
GSList *cells;
guint popup_in_progress : 1;
guint auto_scroll : 1;
guint focus_on_click : 1;
guint button_sensitivity : 2;
+ guint has_entry : 1;
GtkTreeViewRowSeparatorFunc row_separator_func;
gpointer row_separator_data;
PROP_FOCUS_ON_CLICK,
PROP_POPUP_SHOWN,
PROP_BUTTON_SENSITIVITY,
- PROP_EDITING_CANCELED
+ PROP_EDITING_CANCELED,
+ PROP_HAS_ENTRY,
+ PROP_ENTRY_TEXT_COLUMN
};
static guint combo_box_signals[LAST_SIGNAL] = {0,};
static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
+static GObject *gtk_combo_box_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties);
static void gtk_combo_box_dispose (GObject *object);
static void gtk_combo_box_finalize (GObject *object);
static void gtk_combo_box_destroy (GtkWidget *widget);
static void gtk_combo_box_child_hide (GtkWidget *widget,
GtkComboBox *combo_box);
+/* GtkComboBox:has-entry callbacks */
+static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
+ gpointer user_data);
+static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
+ gpointer user_data);
+
+
/* GtkBuildable method implementation */
static GtkBuildableIface *parent_buildable_iface;
GObject *child,
const gchar *tagname,
gpointer *data);
+static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname);
+
/* GtkCellEditable method implementations */
static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
widget_class->destroy = gtk_combo_box_destroy;
object_class = (GObjectClass *)klass;
+ object_class->constructor = gtk_combo_box_constructor;
object_class->dispose = gtk_combo_box_dispose;
object_class->finalize = gtk_combo_box_finalize;
object_class->set_property = gtk_combo_box_set_property;
GTK_SENSITIVITY_AUTO,
GTK_PARAM_READWRITE));
+ /**
+ * GtkComboBox:has-entry:
+ *
+ * Whether the combo box has an entry.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_HAS_ENTRY,
+ g_param_spec_boolean ("has-entry",
+ P_("Has Entry"),
+ P_("Whether combo box has an entry"),
+ FALSE,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GtkComboBox:entry-text-column:
+ *
+ * The column in the combo box's model to associate with strings from the entry
+ * if the combo was created with #GtkComboBox:has-entry = %TRUE.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_ENTRY_TEXT_COLUMN,
+ g_param_spec_int ("entry-text-column",
+ P_("Entry Text Column"),
+ P_("The column in the combo box's model to associate "
+ "with strings from the entry if the combo was "
+ "created with #GtkComboBox:has-entry = %TRUE"),
+ -1, G_MAXINT, -1,
+ GTK_PARAM_READWRITE));
+
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boolean ("appears-as-list",
P_("Appears as list"),
iface->add_child = _gtk_cell_layout_buildable_add_child;
iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
+ iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
}
static void
priv->auto_scroll = FALSE;
priv->focus_on_click = TRUE;
priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
+ priv->has_entry = FALSE;
+
+ priv->text_column = -1;
+ priv->text_renderer = NULL;
gtk_combo_box_check_appearance (combo_box);
}
case PROP_HAS_FRAME:
combo_box->priv->has_frame = g_value_get_boolean (value);
+
+ if (combo_box->priv->has_entry)
+ {
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (GTK_BIN (combo_box));
+
+ gtk_entry_set_has_frame (GTK_ENTRY (child),
+ combo_box->priv->has_frame);
+ }
+
break;
case PROP_FOCUS_ON_CLICK:
combo_box->priv->editing_canceled = g_value_get_boolean (value);
break;
+ case PROP_HAS_ENTRY:
+ combo_box->priv->has_entry = g_value_get_boolean (value);
+ break;
+
+ case PROP_ENTRY_TEXT_COLUMN:
+ gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_boolean (value, priv->editing_canceled);
break;
+ case PROP_HAS_ENTRY:
+ g_value_set_boolean (value, priv->has_entry);
+ break;
+
+ case PROP_ENTRY_TEXT_COLUMN:
+ g_value_set_int (value, priv->text_column);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
GtkComboBox *combo_box = GTK_COMBO_BOX (container);
GtkComboBoxPrivate *priv = combo_box->priv;
+ if (priv->has_entry && !GTK_IS_ENTRY (widget))
+ {
+ g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
+ "(need an instance of GtkEntry or of a subclass)",
+ G_OBJECT_TYPE_NAME (widget));
+ return;
+ }
+
if (priv->cell_view &&
gtk_widget_get_parent (priv->cell_view))
{
priv->box = NULL;
}
}
+
+ if (priv->has_entry)
+ {
+ /* this flag is a hack to tell the entry to fill its allocation.
+ */
+ GTK_ENTRY (widget)->is_cell_renderer = TRUE;
+
+ g_signal_connect (widget, "changed",
+ G_CALLBACK (gtk_combo_box_entry_contents_changed),
+ combo_box);
+
+ gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
+ }
}
static void
GtkTreePath *path;
gboolean appears_as_list;
+ if (priv->has_entry)
+ {
+ GtkWidget *child_widget;
+
+ child_widget = gtk_bin_get_child (GTK_BIN (container));
+ if (widget && widget == child_widget)
+ {
+ g_signal_handlers_disconnect_by_func (widget,
+ gtk_combo_box_entry_contents_changed,
+ container);
+ GTK_ENTRY (widget)->is_cell_renderer = FALSE;
+ }
+ }
+
if (widget == priv->cell_view)
priv->cell_view = NULL;
return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
}
+/**
+ * gtk_combo_box_new_with_entry:
+ *
+ * Creates a new empty #GtkComboBox with an entry.
+ *
+ * Return value: A new #GtkComboBox.
+ */
+GtkWidget *
+gtk_combo_box_new_with_entry (void)
+{
+ return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
+}
+
/**
* gtk_combo_box_new_with_model:
* @model: A #GtkTreeModel.
GtkTreeIter iter;
gchar *text = NULL;
- g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
- g_return_val_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
- == G_TYPE_STRING, NULL);
+ if (combo_box->priv->has_entry)
+ {
+ GtkBin *combo = GTK_BIN (combo_box);
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (combo);
+ if (child)
+ return g_strdup (gtk_entry_get_text (GTK_ENTRY (child)));
- if (gtk_combo_box_get_active_iter (combo_box, &iter))
- gtk_tree_model_get (combo_box->priv->model, &iter,
- 0, &text, -1);
+ return NULL;
+ }
+ else
+ {
+ g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
+ g_return_val_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
+ == G_TYPE_STRING, NULL);
- return text;
+ if (gtk_combo_box_get_active_iter (combo_box, &iter))
+ gtk_tree_model_get (combo_box->priv->model, &iter,
+ 0, &text, -1);
+
+ return text;
+ }
}
static void
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
- gtk_widget_grab_focus (combo_box->priv->button);
+ if (combo_box->priv->has_entry)
+ {
+ GtkWidget* child;
+
+ child = gtk_bin_get_child (GTK_BIN (combo_box));
+ if (child)
+ gtk_widget_grab_focus (child);
+ }
+ else
+ gtk_widget_grab_focus (combo_box->priv->button);
return TRUE;
}
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
- gtk_widget_grab_focus (combo_box->priv->button);
+ if (combo_box->priv->has_entry)
+ {
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (GTK_BIN (combo_box));
+ if (child)
+ gtk_widget_grab_focus (child);
+ }
+ else
+ gtk_widget_grab_focus (combo_box->priv->button);
}
static void
combo_box->priv->cell_view = NULL;
}
+static void
+gtk_combo_box_entry_contents_changed (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
+
+ /*
+ * Fixes regression reported in bug #574059. The old functionality relied on
+ * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
+ * when the selection was already set to -1.
+ */
+ if (gtk_combo_box_get_active(combo_box) == -1)
+ g_signal_emit_by_name (combo_box, "changed");
+ else
+ gtk_combo_box_set_active (combo_box, -1);
+}
+
+static void
+gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
+ gpointer user_data)
+{
+ GtkComboBoxPrivate *priv = combo_box->priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *str = NULL;
+
+ if (gtk_combo_box_get_active_iter (combo_box, &iter))
+ {
+ GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
+
+ if (entry)
+ {
+ g_signal_handlers_block_by_func (entry,
+ gtk_combo_box_entry_contents_changed,
+ combo_box);
+
+ model = gtk_combo_box_get_model (combo_box);
+
+ gtk_tree_model_get (model, &iter,
+ priv->text_column, &str,
+ -1);
+ gtk_entry_set_text (entry, str);
+ g_free (str);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gtk_combo_box_entry_contents_changed,
+ combo_box);
+ }
+ }
+}
+
+static GObject *
+gtk_combo_box_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *object;
+ GtkComboBox *combo_box;
+ GtkComboBoxPrivate *priv;
+
+ object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
+ (type, n_construct_properties, construct_properties);
+
+ combo_box = GTK_COMBO_BOX (object);
+ priv = combo_box->priv;
+
+ if (priv->has_entry)
+ {
+ GtkWidget *entry;
+
+ entry = gtk_entry_new ();
+ gtk_widget_show (entry);
+ gtk_container_add (GTK_CONTAINER (combo_box), entry);
+
+ priv->text_renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer, TRUE);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
+ }
+
+ return object;
+}
+
+
static void
gtk_combo_box_dispose(GObject* object)
{
G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
}
+
static gboolean
gtk_cell_editable_key_press (GtkWidget *widget,
GdkEventKey *event,
}
+/**
+ * gtk_combo_box_get_has_entry:
+ * @combo_box: a #GtkComboBox
+ *
+ * Returns whether the combo box has an entry.
+ *
+ * Return Value: whether there is an entry in @combo_box.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
+{
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
+
+ return combo_box->priv->has_entry;
+}
+
+/**
+ * gtk_combo_box_set_entry_text_column:
+ * @combo_box: A #GtkComboBox.
+ * @text_column: A column in @model to get the strings from for the internal entry.
+ *
+ * Sets the model column which @combo_box should use to get strings from
+ * to be @text_column.
+ *
+ * @combo_box must be created with GtkComboBox:has-entry as %TRUE.
+ *
+ * Since: 3.0
+ */
+void
+gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
+ gint text_column)
+{
+ GtkTreeModel *model;
+
+ g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+ g_return_if_fail (combo_box->priv->has_entry != FALSE);
+
+ model = gtk_combo_box_get_model (combo_box);
+
+ g_return_if_fail (text_column >= 0);
+ g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
+
+ combo_box->priv->text_column = text_column;
+
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
+ combo_box->priv->text_renderer,
+ "text", text_column,
+ NULL);
+}
+
+/**
+ * gtk_combo_box_get_entry_text_column:
+ * @combo_box: A #GtkComboBox.
+ *
+ * Returns the column which @combo_box is using to get the strings
+ * from to display in the internal entry.
+ *
+ * Return value: A column in the data source model of @combo_box.
+ *
+ * Since: 3.0
+ */
+gint
+gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
+{
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
+
+ return combo_box->priv->text_column;
+}
+
/**
* gtk_combo_box_set_focus_on_click:
* @combo: a #GtkComboBox
data);
}
+static GObject *
+gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
+
+ if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
+ return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
+
+ return parent_buildable_iface->get_internal_child (buildable, builder, childname);
+}
static void
gtk_combo_box_remeasure (GtkComboBox *combo_box)